﻿using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using ClusterizationClient.Controller;
using ClusterizationClient.localhost;
using ClusterizationClient.View;
using SharedData;

namespace ClusterizationClient
{
    /// <summary>
    /// Главная форма приложения
    /// </summary>
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            //Получение аргументов командной строки
            string[] args = Environment.CommandLine.Split('-');
            if (args.Length > 1)
            {
                try
                {
                    XmlReader reader = new XmlTextReader(args[1]);
                    XmlSerializer deserialzer = new XmlSerializer(typeof (DataFromDataGrid));
                    DataFromDataGrid receivedData = (DataFromDataGrid) deserialzer.Deserialize(reader);

                    //в цикле добавляем колонки для каждой из характеристик и именуем
                    for (int j = 0; j < receivedData.CharacteristicsNames.Length; j++)
                    {
                        dataGridView5.Columns.Add(new DataGridViewColumn(dataGridView3.Columns[1].CellTemplate));
                        dataGridView5.Columns[j + 2].Width = 150;
                        dataGridView5.Columns[j + 2].HeaderText = receivedData.CharacteristicsNames[j];
                        dataGridView5.Columns[j + 2].SortMode = dataGridView3.Columns[1].SortMode;

                        comboBox2.Items.Add(receivedData.CharacteristicsNames[j]);
                        comboBox3.Items.Add(receivedData.CharacteristicsNames[j]);
                    }

                    // Цикл заполнения таблицы характеристик
                    for (int i = 0; i < receivedData.Names.Length; i++)
                    {
                        dataGridView5.Rows.Add();
                        dataGridView5.Rows[i].Cells[0].Value = receivedData.Names[i];
                        dataGridView5.Rows[i].Cells[1].Value = receivedData.Id[i];
                        for (int m = 0; m < receivedData.Characteristics[i].Count; m++)
                        {
                            dataGridView5.Rows[i].Cells[m + 2].Value = receivedData.Characteristics[i][m];
                        }
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("Произошла ошибка при попытке прочитать файл с данными", e.Message);
                }
            }
        }

        /// <summary>
        /// Контейнер с исходными данными для вычислений
        /// </summary>
        private DataContainer SourceData = null;
        /// <summary>
        /// Контейнер с результатами вычислений
        /// </summary>
        private ResultContainer Result = null;
        /// <summary>
        /// Контроллер
        /// </summary>
        private ClusterizationController Controller = null;

        /// <summary>
        /// События нажатия на кнопку "Выполнить кластеризацию"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            //на время вычислений делаем недоступными элементы управления
            button1.Enabled = false;
            textBox1.Enabled = false;
            comboBox1.Enabled = false;
            groupBox2.Enabled = false;
            comboBox2.Enabled = false;
            comboBox3.Enabled = false;
            button2.Enabled = false;
            dataGridView5.AllowUserToAddRows = false;
            try
            {
                //заполняем контейнер с исходными данными
                SourceData = ParseData();
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                //в случае исключения делаем доступными элементы управления
                EnableMainControls();
                return;
            }
            //и очищаем таблицы с результатами
            dataGridView2.Rows.Clear();
            dataGridView3.Rows.Clear();
            comboBox2.Items.Clear();
            comboBox3.Items.Clear();
            //запускаем вычисления в отдельной нити
            backgroundWorker1.RunWorkerAsync(SourceData);
        }

        /// <summary>
        /// Метод разбора данных с формы
        /// </summary>
        /// <returns>Результаты кластеризации</returns>
        private DataContainer ParseData()
        {
            DataContainer data = new DataContainer();
            //Получаем количество кластеров
            int clusters = Convert.ToInt32(textBox1.Text);
            //выбираем тип разбиения
            switch (comboBox1.Text)
            {
                case "Только заданое количество":
                    data.Type = localhost.MethodClusterization.Simple;
                    break;
                case "Все варианты":
                    data.Type = localhost.MethodClusterization.All;
                    clusters = 2;
                    break;
                case "Все меньше заданного":
                    data.Type = localhost.MethodClusterization.Less;
                    break;
                default:
                    throw new Exception("Неизвестный способ разбиения на таксоны");
            }
            //количество кластеров
            if ((clusters < 2) || (clusters > dataGridView5.Rows.Count))
            {
                throw new Exception("Недопустимое число таксонов");
            }
            data.Clusters = clusters;

            if (dataGridView5.Columns.Count < 3)
            {
                throw new Exception("Нет характеристик");
            }
            //Задаём веса для оценки качества разбиения
            data.PowerWeight = Convert.ToDouble(textBox2.Text);
            data.NormalizedDistanseWeight = Convert.ToDouble(textBox3.Text);
            data.DistanseWeight = Convert.ToDouble(textBox4.Text);

            data.ChainData = new DataFromDataGrid();
            data.ChainData.FillFromDataGrid(dataGridView5);
            return data;
        }

        /// <summary>
        /// Запуск нити осуществляющей кластеризацию
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker Worker = sender as BackgroundWorker;
            Controller = new ClusterizationController();
            e.Result = Controller.Divide((DataContainer)e.Argument);
            if (Worker.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Событие, возникающее по завершении вычислений
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //если вычисления были отменены
            if (e.Cancelled)
            {
                MessageBox.Show("Исследование отменено", "Сообщение", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            //Если произошло исключение
            else if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
            else
            {
                //сохраняем результат
                Result = (ResultContainer) e.Result;
                double OptimalQuality = -1;
                int OptimalNumber = -1;
                SoapClusterizationResult SoapResults;
                //заполнение таблицы с вариантами разбиений и поиск оптимального разбиения
                for (int i = 0; i < Result.Variants.Length; i++)
                {
                    SoapResults = (SoapClusterizationResult)Result.Variants[i];
                    if ((SoapResults.Quality > OptimalQuality) && (!double.IsNaN(SoapResults.Quality)))
                    {
                        OptimalQuality = SoapResults.Quality;
                        OptimalNumber = i;
                    }
                    dataGridView2.Rows.Add();
                    dataGridView2.Rows[i].Cells[0].Value = i + 1;
                    dataGridView2.Rows[i].Cells[1].Value = SoapResults.Clusters.Length;
                    dataGridView2.Rows[i].Cells[2].Value = SoapResults.Quality;
                }

                //заполнение списка характеристик для построения графиков
                comboBox2.Items.Add("");
                comboBox3.Items.Add("");
                //в цикле добавляем колонки для каждой из характеристик и именуем)
                for (int j = 0; j < SourceData.ChainData.CharacteristicsNames.Length; j++)
                {
                    comboBox2.Items.Add(SourceData.ChainData.CharacteristicsNames[j]);
                    comboBox3.Items.Add(SourceData.ChainData.CharacteristicsNames[j]);
                }

                FillDivision(OptimalNumber);
            }

            /*label8.Text = "Время выполнения кластеризации (мс): " +
                          Result.CharacteristicCalculationTime.TotalMilliseconds.ToString();
            label9.Text = "Общее время вычисления (мс): " + Result.OverallCalculationTime.TotalMilliseconds.ToString();
            label8.Visible = true;
            label9.Visible = true;*/

            //делаем доступными элементы управления
            EnableAllControls();
        }

        /// <summary>
        /// Делает доступными все элементы управления, 
        /// в том числе ответственные за вывод графики.
        /// </summary>
        private void EnableAllControls()
        {
            EnableMainControls();
            button2.Enabled = true;
            comboBox2.Enabled = true;
            comboBox3.Enabled = true;
        }

        /// <summary>
        /// Делает доступными основные эелементы управления, 
        /// позволяющие задавать входные данные и запускать вычисления.
        /// </summary>
        private void EnableMainControls()
        {
            button1.Enabled = true;
            if (comboBox1.Text != "Все варианты")
            {
                textBox1.Enabled = true;
            }
            comboBox1.Enabled = true;
            groupBox2.Enabled = true;
        }

        /// <summary>
        /// Если выбираем "все варианты разбиения" число таксонов вводить не нужно
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBox1_TextChanged(object sender, EventArgs e)
        {
            if (comboBox1.Text == "Все варианты")
            {
                textBox1.Enabled = false;
            }
            else
            {
                textBox1.Enabled = true;
            }
        }

        /// <summary>
        /// Проверяем не нажата ли кнопка в таблице с варинтами разбиений 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridView2_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            if((e.RowIndex < 0)||(e.ColumnIndex != 3))
            {
                return;
            }
            FillDivision(e.RowIndex);
        }

        /// <summary>
        /// заполнение таблицы с разбиением
        /// </summary>
        /// <param name="divisionNumber">номер разбиения</param>
        private void FillDivision(int divisionNumber)
        {
            //очистка таблицы
            dataGridView3.Rows.Clear();
            int RowCounter = 0;
            SoapClusterizationResult SoapResults = Result.Variants[divisionNumber];
            for (int j = 0; j < SoapResults.Clusters.Length; j++)
            {
                SoapCluster Cluster = SoapResults.Clusters[j];
                for (int k = 0; k < Cluster.Items.Length; k++)
                {
                    dataGridView3.Rows.Add();
                    dataGridView3.Rows[RowCounter].Cells[0].Value = Cluster.Items[k];
                    //ищем и выводим цепочки с нужными номерами)
                    for (int x = 0; x < SourceData.ChainData.Id.Length; x++)
                    {
                        //если номер цепочки в кластере совпал с номером текущей цепочки из массива
                        //то выводим имя цепочки и переходим на следующий шаг следующего цикла
                        if (Cluster.Items[k] == SourceData.ChainData.Id[x])
                        {
                            dataGridView3.Rows[RowCounter].Cells[1].Value = SourceData.ChainData.Names[x];
                            break;
                        }
                    }
                    dataGridView3.Rows[RowCounter].Cells[2].Value = j + 1;
                    dataGridView3.Rows[RowCounter].DefaultCellStyle.BackColor = Drawer.GetColor(j + 1);
                    RowCounter++;
                }
            }
        }

        /// <summary>
        /// Если нажата кнопка рисования графиков.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            //очищаем полотно
            Drawer.Clear(zedGraphControl3);
            //Если не заданы харатеристики
            if (comboBox2.Text.Equals("") && comboBox3.Text.Equals(""))
            {
                MessageBox.Show("Нет характеристик для построения графиков","Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return;
            }
            //Если задана одна характеристика во втором поле
            if (comboBox2.Text.Equals(""))
            {
                Drawer.DrawGraph(zedGraphControl3,dataGridView5, dataGridView3,comboBox3.Text);
                return;
            }
            //Если задана одна характеристика в первом поле
            if (comboBox3.Text.Equals(""))
            {
                Drawer.DrawGraph(zedGraphControl3, dataGridView5, dataGridView3, comboBox2.Text);
                return;
            }
            //Если заданы обе характеристики
            Drawer.DrawGraph(zedGraphControl3, dataGridView5, dataGridView3,comboBox2.Text, comboBox3.Text);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if(textBox5.Text.Trim().Length == 0)
            {
                MessageBox.Show("Не задано название характеристики", "Невозможно добавить характеристику", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            for (int i = 0; i < dataGridView5.Columns.Count; i++)
            {
                if (dataGridView5.Columns[i].HeaderText.Equals(textBox5.Text))
                {
                    MessageBox.Show("Столбец с таким именем уже существует","Невозможно добавить характеристику", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
            }

            dataGridView5.Columns.Add(new DataGridViewColumn(dataGridView3.Columns[2].CellTemplate));
            dataGridView5.Columns[dataGridView5.Columns.Count - 1].HeaderText = textBox5.Text;
        }
    }
}